맨위로가기

집합 (추상 자료형)

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

집합 (추상 자료형)은 합집합, 교집합, 차집합 등의 연산을 정의하는 추상 자료형으로, 특정 값을 포함하는지 확인하고, 요소 수를 반환하는 등의 기능을 제공한다. 집합은 정적 집합과 동적 집합으로 구분되며, 다양한 자료 구조를 사용하여 구현될 수 있다. 프로그래밍 언어는 집합을 직접 지원하거나, 연관 배열을 사용하여 구현할 수 있다. 집합과 달리 중복된 값을 허용하는 멀티셋(bag)도 존재하며, C++, 자바, 파이썬 등 다양한 언어와 라이브러리에서 지원한다.

2. 집합 연산

집합 대수는 집합의 기본적인 연산을 정의한다. 이러한 연산에는 합집합, 교집합, 차집합, 부분집합 검사 등이 있다.

2. 1. 핵심 집합 연산


  • `union(S, T)`: 집합 ''S''와 ''T''의 합집합을 반환한다.
  • `intersection(S, T)`: 집합 ''S''와 ''T''의 교집합을 반환한다.
  • `difference(S, T)`: 집합 ''S''와 ''T''의 차집합을 반환한다.
  • `subset(S, T)`: 집합 ''S''가 집합 ''T''의 부분집합인지 검사하는 술어다.

2. 2. 정적 집합


  • `is_element_of`(x, S): 값 x가 집합 S에 있는지 확인한다.
  • `is_empty`(S): 집합 S가 비어 있는지 확인한다.
  • `size`(S) 또는 기수(S): S의 요소 수를 반환한다.
  • 반복(S): 각 호출 시 S의 값을 임의의 순서로 하나씩 반환하는 함수를 반환한다.
  • `열거`(S): S의 요소들을 임의의 순서로 포함하는 목록을 반환한다.
  • `build`(x1, x2, …, xn): 값 x1, x2, …, xn을 가진 집합 구조를 생성한다.
  • `create_from`(collection): 주어진 컬렉션의 모든 요소 또는 주어진 반복자에 의해 반환된 모든 요소를 포함하는 새로운 집합 구조를 생성한다.

2. 3. 동적 집합

동적 집합 구조는 일반적으로 다음 연산을 추가한다.

  • `create()`: 새 집합 구조를 생성하며, 초기에는 비어 있다.
  • `create_with_capacity(n)`: 새 집합 구조를 생성하며, 초기에는 비어 있지만 최대 *n*개의 요소를 저장할 수 있다.
  • `add(S, x)`: 요소 *x*가 이미 존재하지 않는 경우, *S*에 추가한다.
  • `remove(S, x)`: 요소 *x*가 존재하는 경우, *S*에서 제거한다.
  • `capacity(S)`: *S*가 담을 수 있는 최대 값의 수를 반환한다.


일부 집합 구조는 이러한 연산 중 일부만 허용할 수 있다. 각 연산의 비용은 구현 방식, 집합에 저장된 특정 값, 그리고 삽입된 순서에 따라 달라질 수 있다.

2. 4. 추가 연산


  • `pop(S)`: `S`에서 임의의 원소를 반환하고, `S`에서 해당 원소를 삭제한다.[1]
  • `pick(S)`: `S`에서 임의의 원소를 반환한다.[2][3][4] `pop`은 `(pick, rest)` 쌍으로 해석될 수 있는데, 여기서 `rest`는 임의의 원소를 제외한 모든 원소로 구성된 집합을 반환한다.[5]
  • map(`F`, `S`): 함수 `F`를 `S`의 각 원소에 적용한 결과로 얻은 고유한 값들의 집합을 반환한다.
  • filter(`P`, `S`): 주어진 술어 `P`를 만족하는 `S`의 모든 원소를 포함하는 부분 집합을 반환한다.
  • fold(`A`0, `F`, `S`): 이진 연산 `F`를 사용하여 `S`의 각 원소 `e`에 대해 `A`i+1 := `F`(`Ai`, `e`)를 적용한 후의 값 `A`|`S`|를 반환한다. `F`가 결합 법칙과 교환 법칙을 만족해야 한다.
  • `clear(S)`: `S`의 모든 원소를 삭제한다.
  • `equal(S`1, `S`2)`: 두 집합이 같은지 (동일한 원소를 모두 포함하는지) 확인한다.
  • `hash(S)`: 정적 집합 `S`에 대한 해시 값을 반환한다. `equal(S`1, `S`2)`이면 `hash(S`1`) = hash(S`2`)`이다.

2. 5. 특수 원소 집합 연산


  • `sum(S)`: "합"의 정의에 따라 *S*의 모든 원소의 합을 반환한다. 예를 들어, 정수 또는 실수에 대해 `fold(0, add, S)`로 정의할 수 있다.
  • `collapse(S)`: 집합의 집합이 주어지면, 합집합을 반환한다.[6] 예를 들어, `collapse() == {1, 2, 3}`이다. 이것은 일종의 `sum`으로 간주할 수 있다.
  • `flatten(S)`: 집합과 원자 원소(집합이 아닌 원소)로 구성된 집합이 주어지면, 원래 최상위 집합의 원자 원소 또는 포함된 집합의 원소를 원소로 갖는 집합을 반환한다. 즉, 중첩 수준을 제거한다. 이는 `collapse`와 유사하지만 원자를 허용한다. 이 작업은 한 번 수행하거나 재귀적으로 수행하여 원자 원소만으로 구성된 집합을 얻을 수 있다.[7] 예를 들어, `flatten({1, {2, 3}}) == {1, 2, 3}`이다.
  • `nearest(S, x)`: *x*에 가장 가까운 *S*의 원소를 반환한다 (일부 거리에 의해).
  • `min(S)`, `max(S)`: *S*의 최소/최대 원소를 반환한다.

3. 타입 이론

타입 이론에서 집합은 일반적으로 해당 지표 함수(특성 함수)와 동일시된다. 따라서 A 타입의 값 집합은 2^{A} 또는 \mathcal{P}(A)로 표기될 수 있다. (하위 타입 및 부분 집합은 세분화 타입으로 모델링될 수 있으며, 몫 집합은 집합체로 대체될 수 있다.) 집합 S의 특성 함수 F는 다음과 같이 정의된다.[1]

:F(x) = \begin{cases}

1, & \mbox{if } x \in S \\

0, & \mbox{if } x \not \in S

\end{cases}


4. 구현

집합은 다양한 자료 구조를 사용하여 구현할 수 있으며, 이는 다양한 연산에 대해 서로 다른 시간과 공간의 트레이드오프를 제공한다. 일부 구현은 `nearest` 또는 `union`과 같은 매우 특수한 연산의 효율성을 개선하도록 설계되었다. "일반 용도"로 설명된 구현은 일반적으로 `element_of`, `add` 및 `delete` 연산을 최적화하려고 한다. 간단한 구현 방법은 리스트를 사용하여 요소의 순서를 무시하고 중복된 값을 피하는 것이다. 이는 간단하지만 비효율적인데, 집합 멤버십 또는 요소 삭제와 같은 연산은 전체 리스트를 스캔해야 하므로 ''O''(''n'')이기 때문이다. 집합은 대신 더욱 효율적인 자료 구조, 특히 다양한 종류의 트리, 트라이, 또는 해시 테이블을 사용하여 구현되는 경우가 많다.

집합은 지표 함수를 통해 일종의 맵으로 해석될 수 있으므로, 각 키-값 쌍의 값이 단위형 또는 센티넬 값(예: 1)인 (부분) 맵(연관 배열)과 동일한 방식으로 집합이 일반적으로 구현된다. 즉, 정렬된 집합의 경우 자체 균형 이진 탐색 트리 (대부분의 연산에 대해 O(log n)이 소요됨), 정렬되지 않은 집합의 경우 해시 테이블 (대부분의 연산에 대해 평균적으로 O(1)이지만 최악의 경우 O(n)이 소요됨)을 사용한다. 정렬된 선형 해시 테이블[8]은 결정적으로 정렬된 집합을 제공하는 데 사용될 수 있다.

또한 맵은 지원하지만 집합은 지원하지 않는 언어에서 맵을 사용하여 집합을 구현할 수 있다.

다른 인기 있는 방법으로는 배열이 있다. 특히 정수 1..''n''의 부분 집합은 ''n'' 비트의 비트 배열로 효율적으로 구현할 수 있으며, 이는 매우 효율적인 합집합 및 교집합 연산도 지원한다. 블룸 맵은 매우 콤팩트한 표현을 사용하여 집합을 확률적으로 구현하지만 쿼리에 대해 작은 오탐 가능성을 감수한다.

부울 집합 연산은 더 기본적인 연산(pop, clearadd)으로 구현할 수 있지만, 특수 알고리즘은 더 낮은 점근적 시간 경계를 생성할 수 있다. 예를 들어, 집합이 정렬된 리스트로 구현된 경우 union(''S'',''T'')에 대한 단순한 알고리즘은 ''S''의 길이 ''m''과 ''T''의 길이 ''n''에 비례하는 시간이 걸린다. 반면, 리스트 병합 알고리즘의 변형은 ''m''+''n''에 비례하는 시간에 작업을 수행한다. 또한 다른 연산을 희생하여 이러한 연산 중 하나 이상에 최적화된 특수 집합 자료 구조(예: 합집합-찾기 자료 구조)가 있다.

5. 프로그래밍 언어 지원


  • 파스칼은 집합을 지원하는 초기 언어 중 하나였으며, 현재 많은 언어가 핵심 언어 또는 표준 라이브러리에 집합을 포함하고 있다.
  • C++표준 템플릿 라이브러리(STL)는 `set` 템플릿 클래스를 제공하며, 이는 일반적으로 레드-블랙 트리와 같은 이진 검색 트리를 사용하여 구현된다. SGI의 STL은 해시 테이블을 사용하는 `hash_set` 템플릿 클래스도 제공한다. C++11은 해시 테이블을 사용하는 `unordered_set` 템플릿 클래스를 지원한다. 집합의 요소는 키 역할을 하며, (상대적 또는 절대적) 위치를 사용하여 액세스되는 시퀀스 컨테이너와는 다르다. 집합 요소는 엄격한 약한 순서를 가져야 한다.
  • 러스트 표준 라이브러리는 제네릭 [https://doc.rust-lang.org/std/collections/struct.HashSet.html HashSet] 및 [https://doc.rust-lang.org/std/collections/struct.BTreeSet.html BTreeSet] 유형을 제공한다.
  • 자바는 인터페이스를 제공하여 집합을 지원하며 ( `HashSet` 클래스는 해시 테이블을 사용하여 구현), `SortedSet` 하위 인터페이스는 정렬된 집합을 지원한다 (`TreeSet` 클래스는 이진 검색 트리를 사용하여 구현).
  • 애플의 파운데이션 프레임워크(코코아의 일부)는 Objective-C 클래스 [https://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSSet_Class/ NSSet], [https://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableSet_Class/ NSMutableSet], [https://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSCountedSet_Class/ NSCountedSet], [https://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSOrderedSet_Class/Reference/Reference.html NSOrderedSet] 및 [https://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSMutableOrderedSet_Class/Reference/Reference.html NSMutableOrderedSet]을 제공한다. CoreFoundation API는 C에서 사용할 수 있는 [https://developer.apple.com/documentation/CoreFoundation/Reference/CFSetRef/ CFSet] 및 [https://developer.apple.com/documentation/CoreFoundation/Reference/CFMutableSetRef/ CFMutableSet] 유형을 제공한다.
  • 파이썬은 2.4 이후부터 기본 제공 [https://docs.python.org/library/stdtypes.html#set-types-set-frozenset `set` 및 `frozenset` 유형]을 가지고 있으며, 파이썬 3.0 및 2.7 이후부터는 중괄호 구문(예: `{x, y, z}`)을 사용하여 비어 있지 않은 집합 리터럴을 지원한다. 빈 집합은 `set()`을 사용하여 생성해야 하는데, 이는 파이썬이 빈 딕셔너리를 나타내기 위해 `{}`를 사용하기 때문이다.
  • .NET 프레임워크는 제네릭 [http://msdn.microsoft.com/en-us/library/bb359438.aspx HashSet] 및 [http://msdn.microsoft.com/en-us/library/dd412070.aspx SortedSet] 클래스를 제공하며, 이들은 제네릭 [http://msdn.microsoft.com/en-us/library/dd412081.aspx ISet] 인터페이스를 구현한다.
  • 스몰토크 클래스 라이브러리는 `Set` 및 `IdentitySet`을 포함하며, 각각 동등성 및 ID를 사용하여 포함 테스트를 수행한다. 많은 방언이 압축된 저장소(`NumberSet`, `CharacterSet`), 정렬(`OrderedSet`, `SortedSet` 등) 또는 약한 참조 (`WeakIdentitySet`)를 위한 변형을 제공한다.
  • 루비 표준 라이브러리는 해시 테이블을 사용하여 집합을 구현하는 `Set` 및 `SortedSet` 클래스를 포함하는 [http://ruby-doc.org/stdlib/libdoc/set/rdoc/index.html set] 모듈을 포함하며, 후자는 정렬된 순서로 반복을 허용한다.
  • OCaml 표준 라이브러리에는 이진 검색 트리를 사용하여 함수형 집합 데이터 구조를 구현하는 `Set` 모듈이 포함되어 있다.
  • GHC의 하스켈 구현은 이진 검색 트리를 사용하여 불변 집합을 구현하는 `[https://hackage.haskell.org/package/containers/docs/Data-Set.html Data.Set]` 모듈을 제공한다.[9]
  • Tcl Tcllib 패키지는 TCL 목록을 기반으로 집합 데이터 구조를 구현하는 집합 모듈을 제공한다.
  • 스위프트 표준 라이브러리에는 스위프트 1.2부터 `Set` 유형이 포함되어 있다.
  • 자바스크립트는 ECMAScript 2015[10] 표준을 통해 표준 내장 객체로 `[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set Set]`을 도입했다.
  • Erlang 표준 라이브러리에는 `[http://erlang.org/doc/man/sets.html sets]` 모듈이 있다.
  • 클로저는 해시된 집합에 대한 리터럴 구문을 가지며 정렬된 집합도 구현한다.
  • 랩뷰는 버전 2019부터 집합을 네이티브로 지원한다.
  • 에이다는 `[http://www.ada-auth.org/standards/12rm/html/RM-A-18-8.html Ada.Containers.Hashed_Sets]` 및 `[http://www.ada-auth.org/standards/12rm/html/RM-A-18-9.html Ada.Containers.Ordered_Sets]` 패키지를 제공한다.


집합을 직접 지원하지 않지만 연관 배열을 지원하는 언어에서는 요소를 키로 사용하고 더미 값을 값으로 사용하여 집합을 에뮬레이션할 수 있으며, 이 값은 무시된다.

6. 중복 데이터 처리

데이터의 동일성은 주어진 비교 함수로 판단되므로, 외부 문맥에서 동일한지는 함수에 의존한다. 예를 들어 문자열 "hoge""HOGE"는 다른 데이터로 볼 수도 있고, 대소문자를 구별하지 않고 비교하면 (항상 대문자화 또는 소문자화한 것끼리 비교하면) 동일한 데이터로 볼 수도 있다.

이러한 중복된 데이터를 삽입하려고 시도한 경우에는 다음처럼 처리할 수 있다.


  • 무시한다.
  • 새로운 것으로 교체한다.
  • 다중화한다. (멀티셋 참조)


협의의 집합에서는 중복 데이터는 무시되거나 새로운 데이터로 교체된다. 만약 여기서 다중화를 선택한 경우에는 여러 번의 삭제를 수행해야 값이 완전히 제거된다.

7. 다른 추상 자료형과의 차이점


  • 리스트는 각 데이터에 순서가 정의된다는 점에서 집합과 다르다.
  • 배열은 순서가 정의될 뿐만 아니라, 정적 배열의 경우 저장 가능한 요소 수도 변경할 수 없다는 점이 집합과 다르다.
  • 멀티셋(bag이라고도 불림)은 같은 데이터를 여러 개 저장할 수 있지만, 좁은 의미의 집합에서는 중복된 데이터는 무시된다.


관계형 데이터베이스에서 테이블은 일부 열에 고유성 제약 조건(이는 테이블을 후보 키로 만듦)이 있는지 여부에 따라 (수학적) 집합 또는 멀티셋이 될 수 있다.[1]

SQL은 관계형 테이블에서 행을 선택할 수 있도록 한다.[1] 이 연산은 일반적으로 멀티셋을 생성하지만, `DISTINCT` 키워드를 사용하여 모든 행이 서로 다르도록 강제하거나, 선택에 기본 키(또는 후보 키)가 포함된 경우는 예외이다.[1]

ANSI SQL에서 `MULTISET` 키워드는 하위 쿼리를 컬렉션 표현식으로 변환하는 데 사용할 수 있다.[1]

```sql

SELECT expression1, expression2... FROM table_name...

```

는 다른 보다 일반적인 쿼리의 ''하위 쿼리 표현식''으로 사용할 수 있는 일반적인 선택 쿼리인 반면,[1]

```sql

MULTISET(SELECT expression1, expression2... FROM table_name...)

```

는 하위 쿼리를 다른 쿼리에서 사용하거나 적절한 컬렉션 유형의 열에 할당할 수 있는 ''컬렉션 표현식''으로 변환한다.[1]

8. 멀티셋 (Multiset)

'''멀티셋'''(multiset) 또는 '''가방(bag)'''은 집합과 유사하지만, 집합과 달리 중복된("같은") 값을 허용하는 자료구조이다. 멀티셋은 같은 값을 단순히 세는 "동일한" 것으로 간주하거나, 같은 값을 "동등한" 것으로 간주하여 별개의 항목으로 저장하는 두 가지 방식으로 사용된다.

예를 들어, 사람 목록(이름별)과 나이(년 단위)가 주어졌을 때, 특정 연령의 사람 수를 단순히 세는 나이의 멀티셋을 구성할 수 있다. 또는, 나이가 같으면 두 사람이 동등하다고 간주되지만 다른 사람이고 다른 이름을 가질 수 있는 사람의 멀티셋을 구성할 수 있다. 이 경우 각 쌍(이름, 나이)을 저장해야 하며, 특정 나이를 선택하면 해당 나이의 모든 사람을 얻을 수 있다.

형식적으로, 컴퓨터 과학에서 객체는 어떤 동치 관계 하에서 "같다"고 간주될 수 있지만, 다른 관계 하에서는 여전히 별개로 간주될 수 있다. 일부 멀티셋 구현은 별개의 동일한 객체를 데이터 구조의 별도 항목으로 저장하는 반면, 다른 유형은 단일 버전(처음 발견된 것)으로 축소하고 요소의 개수를 양의 정수로 유지한다.

집합과 마찬가지로, 멀티셋은 해시 테이블이나 트리를 사용하여 구현할 수 있으며, 이는 서로 다른 성능 특성을 제공한다.

유형 T에 대한 모든 가방의 집합은 bag T 식으로 주어진다. 멀티셋을 동일한 항목을 동일하다고 간주하고 단순히 센다면, 멀티셋은 입력 도메인에서 음이 아닌 정수(자연수)로의 함수로 해석할 수 있으며, 이는 집합을 지표 함수로 식별하는 것을 일반화한다. 어떤 경우에는 이 계산 의미의 멀티셋이 파이썬에서와 같이 음수 값을 허용하도록 일반화될 수 있다.

여러 프로그래밍 언어에서 멀티셋을 지원하거나, 멀티셋을 지원하지 않는 경우에도 해결 방법이 존재한다.

프로그래밍 언어멀티셋 지원 여부 및 방법
C++표준 템플릿 라이브러리에서 정렬된 멀티셋(멀티셋)과 정렬되지 않은 멀티셋(unordered_multiset)을 모두 구현한다.
자바타사 라이브러리가 멀티셋 기능을 제공한다.
Apple코코아의 일부로 NSCountedSet 클래스를 제공하며, CoreFoundation의 일부로 CFBagCFMutableBag 유형을 제공한다.
파이썬표준 라이브러리에 멀티셋과 유사한 collections.Counter가 포함되어 있다.
스몰토크포함 테스트에 대한 술어로 ID 또는 동등성을 사용할 수 있도록 인스턴스화할 수 있는 Bag 클래스가 포함되어 있다.
멀티셋 미지원 시일반 집합을 사용하고 항목의 동등성 술어를 재정의하여 별개의 객체에 대해 항상 "같지 않음"을 반환하도록 하거나(단, 동일 객체의 여러 발생을 저장할 수 없음), 값을 정수 개수로 매핑하는 연관 배열을 사용한다(동일한 요소 간의 차이를 구별할 수 없음).



가방에 대한 일반적인 연산은 다음과 같다.


  • `contains(''B'', ''x'')`: 요소 ''x''가 가방 ''B''에 (적어도 한 번) 존재하는지 확인
  • `is_sub_bag(''B''1, ''B''2)`: 가방 ''B''1의 각 요소가 가방 ''B''2에서 발생하는 빈도보다 더 자주 ''B''1에서 발생하는지 확인. 때로는 ''B''1 ⊑ ''B''2로 표기한다.
  • `count(''B'', ''x'')`: 요소 ''x''가 가방 ''B''에 발생하는 횟수를 반환한다. 때로는 ''B'' # ''x''로 표기한다.
  • `scaled_by(''B'', ''n'')`: 자연수 ''n''이 주어지면, 가방 ''B''와 동일한 요소를 포함하는 가방을 반환하며, 가방 ''B''에서 ''m''번 발생하는 모든 요소가 결과 가방에서 ''n'' * ''m''번 발생하는 것을 제외한다. 때로는 ''n'' ⊗ ''B''로 표기한다.
  • `union(''B''1, ''B''2)`: 가방 ''B''1 또는 가방 ''B''2에서 발생하는 값만 포함하는 가방을 반환하지만, 결과 가방에서 값 ''x''가 발생하는 횟수는 (''B''1 # x) + (''B''2 # x)와 같다. 때로는 ''B''1 ⊎ ''B''2로 표기한다.

참조

[1] 문서 pop() https://docs.python.[...] Python
[2] 서적 Management and Processing of Complex Data Structures: Third Workshop on Information Systems and Artificial Intelligence, Hamburg, Germany, February 28 - March 2, 1994. Proceedings https://books.google[...]
[3] 문서 Issue7212 http://bugs.python.o[...] Python
[4] 문서 Feature #4553 https://bugs.ruby-la[...] Ruby
[5] 서적 Inductive Synthesis of Functional Programs: Universal Planning, Folding of Finite Programs, and Schema Abstraction by Analogical Reasoning https://books.google[...] Springer 2003-08-21
[6] 서적 Recent Trends in Data Type Specification: 10th Workshop on Specification of Abstract Data Types Joint with the 5th COMPASS Workshop, S. Margherita, Italy, May 30 - June 3, 1994. Selected Papers, Volume 10 https://books.google[...]
[7] 문서 flatten() http://www.ruby-doc.[...] Ruby
[8] 간행물 Sorted Linear Hash Table http://www.concentri[...]
[9] 뉴스 Efficient sets: a balancing act http://www.swiss.ai.[...] Journal of Functional Programming 1993-10
[10] 웹사이트 ECMAScript 2015 Language Specification – ECMA-262 6th Edition http://www.ecma-inte[...] 2017-07-11
[11] 문서 4. 組み込み型 — Python 3.6.5 ドキュメント https://docs.python.[...]
[12] 문서 library set (Ruby 2.1.0) http://docs.ruby-lan[...]



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com